package com.sunny.springboot2.core.interceptor;

import com.sunny.springboot2.core.annotation.Partition;
import com.sunny.springboot2.entity.DBPartition;
import com.sunny.springboot2.hash.PartitionByMurmurHash;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.DirectFieldAccessor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.AnnotationUtils;

import java.lang.reflect.Method;
import java.util.Arrays;

/**
 * hash散列处理
 *
 * @author Sunny
 * @date 2019/5/17 11:55
 */
@Aspect
@Configuration
public class PartitionAspect {

    private final static Logger LOG = LoggerFactory.getLogger(PartitionAspect.class);

    @Autowired
    private PartitionByMurmurHash murmurHash;

    @Pointcut("@annotation(com.sunny.springboot2.core.annotation.Partition)")
    public void pointCut(){	}

    @Around("pointCut()")
    public Object routeWith(ProceedingJoinPoint joinPoint) throws Throwable {
        MethodSignature signature = (MethodSignature)joinPoint.getSignature();
        String qualifiedName = signature.getDeclaringTypeName().concat(".").concat(signature.getName());
        Object[] args = null;
        long begin = System.currentTimeMillis();
        try {
            Method method =  signature.getMethod();
            Partition partition = AnnotationUtils.findAnnotation(method, Partition.class);
            String key = partition.key();
            args = joinPoint.getArgs();
            for(Object o : args) {
                if(o instanceof DBPartition) {
                    DirectFieldAccessor fieldAccessor = new DirectFieldAccessor(o);
                    String value = (String) fieldAccessor.getPropertyValue(key);
                    ((DBPartition)o).setTabIndex(computeTabIndex(value));
                }
            }
            return joinPoint.proceed();
        }  catch (Throwable e) {
            LOG.error("invoke method[{}] failed!", qualifiedName, e);
            throw e;
        } finally {
            LOG.info(String.format("invoke method[%s] with args:%s end!cost:%sms", qualifiedName, Arrays.asList(args),System.currentTimeMillis()- begin));
        }
    }

    private int computeTabIndex(String partitionKey) {
        Integer tableIndex = murmurHash.calculate(partitionKey);
        LOG.info("through partitionKey [{}] compute table index is [{}]!",partitionKey, tableIndex);
        return tableIndex;
    }
}
